package com.moss.cloud.base.biz.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.moss.cloud.base.api.model.from.ApprovalFrom;
import com.moss.cloud.base.api.model.from.BaseFrom;
import com.moss.cloud.base.api.model.from.FlowBusinessFrom;
import com.moss.cloud.base.api.model.from.TaskFrom;
import com.moss.cloud.base.api.model.param.FlowBusinessParam;
import com.moss.cloud.base.api.model.vo.FlowBusinessVO;
import com.moss.cloud.base.api.model.vo.FlowTaskVO;
import com.moss.cloud.base.biz.mapper.FlowBusinessMapper;
import com.moss.cloud.base.biz.model.FlowBusiness;
import com.moss.cloud.base.biz.model.FlowModel;
import com.moss.cloud.base.biz.model.FlowTask;
import com.moss.cloud.base.biz.service.IFlowBusinessService;
import com.moss.cloud.base.biz.service.IFlowModelService;
import com.moss.cloud.base.biz.service.IFlowTaskService;
import com.moss.cloud.base.biz.service.ISysUserService;
import com.moss.cloud.base.biz.utils.DataProUtil;
import com.moss.cloud.common.base.model.po.SysUser;
import com.moss.cloud.common.base.utils.PageUtil;
import com.moss.cloud.common.core.exception.SystemErrorType;
import com.moss.cloud.common.core.utils.BooleanHandel;
import com.moss.cloud.common.core.utils.UserContextHolder;
import com.moss.cloud.common.flow.enums.FlowStatus;
import com.moss.cloud.common.flow.enums.ModelCompletionMethodEnum;
import com.moss.cloud.common.flow.enums.Target;
import com.moss.cloud.common.flow.enums.TaskState;
import com.moss.cloud.common.flow.executor.TaskExecutor;
import com.moss.cloud.common.flow.model.NodeTask;
import com.moss.cloud.common.flow.parser.TaskParser;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 业务流程业务实现
 *
 * @author 瑾年
 * &#064;date  2024-03-06
 */
@Slf4j
@Service
@EnableAspectJAutoProxy(exposeProxy = true)
public class FlowBusinessServiceImpl extends ServiceImpl<FlowBusinessMapper, FlowBusiness> implements IFlowBusinessService {
    private final IFlowModelService flowModelService;
    private final IFlowTaskService flowTaskService;
    private final ISysUserService sysUserService;

    public FlowBusinessServiceImpl(IFlowModelService flowModelService, IFlowTaskService flowTaskService,
                                   ISysUserService sysUserService) {
        this.flowModelService = flowModelService;
        this.flowTaskService = flowTaskService;
        this.sysUserService = sysUserService;
    }

    /**
     * 分页查询
     *
     * @param param 业务流程查询参数
     * @return IPage<FlowBusinessVO>
     */
    @Override
    public IPage<FlowBusinessVO> page(FlowBusinessParam param) {
        Page<FlowBusiness> page = PageUtil.createPage(param.getCurrent(), param.getSize());
        return baseMapper.flowBusinessPage(page, UserContextHolder.getTenantId(), param, UserContextHolder.getUserId());
    }

    /**
     * 新增
     *
     * @param from 业务流程表单对象
     * @return boolean
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveFlowBusiness(FlowBusinessFrom from) {
        FlowBusiness flowBusiness = new FlowBusiness();
        BeanUtils.copyProperties(from, flowBusiness);
        flowBusiness.setTenantId(UserContextHolder.getTenantId());
        flowBusiness.setUserId(UserContextHolder.getUserId());
        this.save(flowBusiness);
        from.setId(flowBusiness.getId());
        ((FlowBusinessServiceImpl) AopContext.currentProxy()).saveFlowTask(from);
        this.startFlowTask(flowBusiness.getId());
        return Boolean.TRUE;
    }

    /**
     * 批量保存任务
     *
     * @param from 业务流程表单对象
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveFlowTask(FlowBusinessFrom from) {
        from.getNodeTasks().removeIf(item -> Boolean.TRUE.equals(item.getIsApplicant()));
        FlowModel flowModel = flowModelService.getById(from.getFlowModelId());
        SysUser sysUser = sysUserService.getById(UserContextHolder.getUserId());
        List<FlowTask> flowTasks = from.getNodeTasks().stream().map(item -> {
            FlowTask flowTask = new FlowTask();
            BeanUtils.copyProperties(item, flowTask);
            flowTask.setBusId(from.getId());
            flowTask.setStatus(TaskState.NO_START.getType());
            flowTask.setBusId(from.getId());
            flowTask.setHeadUsers(this.userIds(item.getApprovalUsers()));
            flowTask.setCcToUsers(this.userIds(item.getCcUsers()));
            flowTask.setTitle(sysUser.getName() + "发起" + flowModel.getFlowModelName());
            return flowTask;
        }).collect(Collectors.toList());
        flowTaskService.saveBatchFlowTask(flowTasks);
    }

    /**
     * 转换一下审批人，抄送人数据
     *
     * @param approvalUsers FlowTaskVO.ApprovalUser对象集合
     * @return String
     */
    private String userIds(List<FlowTaskVO.ApprovalUser> approvalUsers) {
        return approvalUsers.stream()
                .map(FlowTaskVO.ApprovalUser::getId) // 获取用户 ID
                .map(String::valueOf) // 将 Long 转换为 String
                .collect(Collectors.joining(","));
    }

    /**
     * 更新
     *
     * @param from 业务流程表单对象
     * @return boolean
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean update(FlowBusinessFrom from) {
        BooleanHandel.trueThrow(Objects.isNull(from.getId())).throwMessage(SystemErrorType.PRIMARY_KEY);
        FlowBusiness flowBusiness = this.getById(from.getId());
        this.checkData(flowBusiness);
        FlowBusiness model = new FlowBusiness();
        BeanUtils.copyProperties(from, model);
        this.updateById(model);
        flowTaskService.removeBatchFlowTask(from.getId());
        ((FlowBusinessServiceImpl) AopContext.currentProxy()).saveFlowTask(from);
        this.startFlowTask(flowBusiness.getId());
        return Boolean.TRUE;
    }


    /**
     * 删除
     *
     * @param baseFrom 公共表单对象
     * @return boolean
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean remove(BaseFrom baseFrom) {
        FlowBusiness flowBusiness = this.getById(baseFrom.getId());
        this.checkData(flowBusiness);
        boolean flag = this.removeById(baseFrom.getId());
        return flag ? flowTaskService.removeBatchFlowTask(baseFrom.getId()) : Boolean.FALSE;
    }

    /**
     * 验证数据所属权
     *
     * @param flowBusiness 业务流程对象
     */
    private void checkData(FlowBusiness flowBusiness) {
        BooleanHandel.trueThrow(Objects.isNull(flowBusiness)).throwMessage(SystemErrorType.DATE_EMPTY);
        BooleanHandel.trueThrow(!flowBusiness.getTenantId().equals(UserContextHolder.getTenantId())).throwMessage(SystemErrorType.NOT_BELONG_TENANT);
    }

    /**
     * 详情
     *
     * @param baseFrom 公共表单对象
     * @return FlowBusinessVO
     */
    @Override
    public FlowBusinessVO detail(BaseFrom baseFrom) {
        FlowBusinessVO flowBusinessVO = baseMapper.flowBusinessDetail(UserContextHolder.getTenantId(), baseFrom.getId());
        List<FlowTask> flowTasks = flowTaskService.list(new QueryWrapper<FlowTask>().lambda().eq(FlowTask::getBusId, baseFrom.getId()));
        SysUser user = sysUserService.getById(flowBusinessVO.getUserId());
        List<FlowTaskVO> flowTaskVOS = new ArrayList<>();
        // 把申请人添加到节点中
        flowTaskVOS.add(DataProUtil.addApplyFlowUser(user.getName(), user.getId(), user.getAvatar(), flowBusinessVO.getCreatedTime()));
        List<FlowTaskVO> taskVOS = CollectionUtils.isNotEmpty(flowTasks) ? flowTasks.stream().map(item -> {
            FlowTaskVO flowTaskVO = new FlowTaskVO();
            BeanUtils.copyProperties(item, flowTaskVO);
            flowTaskVO.setApprovalUsers(flowTaskService.approvalUsers(Arrays.stream(item.getHeadUsers().split(",")).map(Long::valueOf)
                    .collect(Collectors.toList())));
            flowTaskVO.setCcUsers(flowTaskService.approvalUsers(Arrays.stream(item.getCcToUsers().split(",")).map(Long::valueOf)
                    .collect(Collectors.toList())));
            return flowTaskVO;
        }).collect(Collectors.toList()) : Collections.emptyList();
        flowTaskVOS.addAll(taskVOS);
        flowBusinessVO.setNodeTasks(flowTaskVOS);
        return flowBusinessVO;
    }

    /**
     * 获取任务节点
     *
     * @param from 任务节点对象
     * @return List<FlowTaskVO>
     */
    @Override
    public List<FlowTaskVO> nodeTasks(TaskFrom from) {
        FlowModel flowModel = flowModelService.getById(from.getModelId());
        List<NodeTask> nodeTasks = new ArrayList<>();
        // 把申请人添加到节点中
        SysUser user = sysUserService.getById(UserContextHolder.getUserId());
        nodeTasks.add(DataProUtil.addApplyUser(user.getName(), user.getId()));
        List<NodeTask> tasks = TaskParser.taskNodes(flowModel.getFlowData(), from.getFlowForm());
        nodeTasks.addAll(tasks);
        return CollectionUtils.isNotEmpty(nodeTasks) ? nodeTasks.stream().map(item -> {
            FlowTaskVO flowTaskVO = new FlowTaskVO();
            BeanUtils.copyProperties(item, flowTaskVO);
            flowTaskVO.setApprovalUsers(flowTaskService.approvalUsers(item.getHeadUsers()));
            flowTaskVO.setCcUsers(flowTaskService.approvalUsers(item.getCcToUsers()));
            return flowTaskVO;
        }).collect(Collectors.toList()) : Collections.emptyList();
    }


    /**
     * 业务流程创建完成后调用开始任务
     *
     * @param busId 当前业务流程ID
     */
    private void startFlowTask(Long busId) {
        List<FlowTask> flowTasks = flowTaskService.list(new QueryWrapper<FlowTask>().lambda().eq(FlowTask::getBusId, busId));
        FlowTask flowTask = flowTasks.stream().findFirst().orElse(null);
        BooleanHandel.isTrue(Objects.nonNull(flowTask)).trueHandle(() -> {
            NodeTask nodeTask = this.convertNodeTask(flowTask, Objects.requireNonNull(flowTask).getStatus());
            TaskExecutor.build().process(nodeTask);
        });
    }

    /**
     * 流程审批
     *
     * @param approvalFrom 审批对象
     * @return boolean
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean approval(ApprovalFrom approvalFrom) {
        Long userId = UserContextHolder.getUserId();
        FlowTask flowTask = flowTaskService.getById(approvalFrom.getId());
        BooleanHandel.trueThrow(Objects.isNull(flowTask)).throwMessage(SystemErrorType.DATE_EMPTY);
        // 不是进行中的任务无法审批
        BooleanHandel.trueThrow(!TaskState.IN_PROGRESS.getType().equals(flowTask.getStatus())).throwMessage(SystemErrorType.NO_IN_PROGRESS_TASK);
        // 转换审批人ID
        List<Long> headUserIds = Arrays.stream(flowTask.getHeadUsers().split(",")).map(Long::valueOf)
                .collect(Collectors.toList());
        // 匹配当前从啊做人ID是否在审批人中
        boolean exists = headUserIds.stream().anyMatch(id -> id.equals(userId));
        // 不存在无数据操作权限
        BooleanHandel.trueThrow(!exists).throwMessage(SystemErrorType.NO_OPERATION_PERMISSION);
        FlowBusiness flowBusiness = this.getById(flowTask.getBusId());
        BooleanHandel.trueThrow(Objects.isNull(flowBusiness)).throwMessage(SystemErrorType.DATE_EMPTY);
        BooleanHandel.trueThrow(!FlowStatus.IN_PROGRESS.getType().equals(flowBusiness.getFlowStatus())).throwMessage(SystemErrorType.NO_IN_PROGRESS);
        NodeTask nodeTask = this.convertNodeTask(flowTask, approvalFrom.getStatus());
        nodeTask.setApplyOpinion(approvalFrom.getApplyOpinion());
        TaskExecutor.build().process(nodeTask);
        return Boolean.TRUE;
    }

    /**
     * 转换成NodeTask对象
     *
     * @param flowTask 当前审批节点任务
     * @param status   当前审批节点状态
     * @return NodeTask
     */
    private NodeTask convertNodeTask(FlowTask flowTask, Integer status) {
        NodeTask nodeTask = new NodeTask();
        BeanUtils.copyProperties(flowTask, nodeTask);
        nodeTask.setHeadUsers(Arrays.stream(flowTask.getHeadUsers().split(",")).map(Long::valueOf)
                .collect(Collectors.toList()));
        nodeTask.setCcToUsers(Arrays.stream(flowTask.getCcToUsers().split(",")).map(Long::valueOf)
                .collect(Collectors.toList()));
        nodeTask.setStatus(status);
        return nodeTask;
    }

    /**
     * 下一个任务
     *
     * @param flowTask 当前审批节点任务
     */
    @Override
    public void nextTask(FlowTask flowTask) {
        FlowBusiness flowBusiness = this.flowBusinessStatus(flowTask);
        // 如果下一个节点是结束节点或者流程模型配置的是一个节点拒绝结束流程，直接调用finish结束流程
        // 否则根据当前节点查询到下一个节点，调用任务执行过程
        BooleanHandel.isTureOrFalse(Target.END.getType().equals(flowTask.getTarget()) ||
                        FlowStatus.REFUSE.getType().equals(flowBusiness.getFlowStatus()))
                .trueOrFalseHandle(() -> ((FlowBusinessServiceImpl) AopContext.currentProxy())
                        .finish(flowTask, flowBusiness.getFlowStatus()), () -> {
                    FlowTask nextTask = flowTaskService.getOne(new QueryWrapper<FlowTask>().lambda()
                            .eq(FlowTask::getTaskId, flowTask.getTarget())
                            .eq(FlowTask::getBusId, flowTask.getBusId()));
                    BooleanHandel.trueThrow(Objects.isNull(nextTask)).throwMessage(SystemErrorType.DATE_EMPTY);
                    this.nodeProcess(nextTask);
                });
    }

    /**
     * 下一个节点过程
     *
     * @param nextTask 下一个节点任务
     */
    public void nodeProcess(FlowTask nextTask) {
        // 根据模型配置和status状态判断如果是其中一个节点不通过直接结束的话调用finish方法即可
        NodeTask nodeTask = this.convertNodeTask(nextTask, nextTask.getStatus());
        TaskExecutor.build().process(nodeTask);
    }

    /**
     * 流程结束
     *
     * @param flowTask 当前审批节点任务
     * @param status   流程状态
     */
    @Transactional(rollbackFor = Exception.class)
    public void finish(FlowTask flowTask, Integer status) {
        FlowBusiness flowBusiness = new FlowBusiness();
        flowBusiness.setId(flowTask.getBusId());
        flowBusiness.setFlowStatus(status);
        log.info("任务结束后发布一个任务流程完结的通知信息:{}", flowTask.getBusId());
        this.updateById(flowBusiness);
    }

    /**
     * 根据当前节点返回流程模型配置的结束方式
     * 1全部通过
     * 2少数服从多数，多数通过流程通过
     * 3指定人通过流程通过
     * 4流程模型配置的有一个节点审批不通过直接结束流程
     *
     * @param flowTask 当前审批节点任务
     * @return FlowBusiness
     */
    private FlowBusiness flowBusinessStatus(FlowTask flowTask) {
        FlowBusiness flowBusiness = this.getById(flowTask.getBusId());
        FlowModel flowModel = flowModelService.getById(flowBusiness.getFlowModelId());
        // 全部通过
        BooleanHandel.isTrue(ModelCompletionMethodEnum.ALL_PASS.getType().equals(flowModel.getFlowModelCompletionMethod()))
                .trueHandle(() -> this.allPassFlowStatus(flowTask, flowBusiness));
        // 少数服从多数，多数通过流程通过
        BooleanHandel.isTrue(ModelCompletionMethodEnum.MAJORITY_RULE.getType().equals(flowModel.getFlowModelCompletionMethod()))
                .trueHandle(() -> this.majorityFlowStatus(flowTask, flowBusiness));
        // 指定人通过流程通过
        BooleanHandel.isTrue(ModelCompletionMethodEnum.MODEL_ADMIN.getType().equals(flowModel.getFlowModelCompletionMethod()))
                .trueHandle(() -> this.modelAdminFlowStatus(flowTask, flowModel, flowBusiness));
        // 流程模型配置的有一个节点审批不通过直接结束流程
        BooleanHandel.isTrue(ModelCompletionMethodEnum.TASK_NODE_PASS.getType().equals(flowModel.getFlowModelCompletionMethod()))
                .trueHandle(() -> flowBusiness.setFlowStatus(FlowStatus.REFUSE.getType()));
        return flowBusiness;
    }

    /**
     * 全部通过的处理
     * 当前节点的下一个节点是结束的并且当前节点是通过的设置为通过
     * 否则当前节点的下一个节点是结束的，当前节点的状态不是通过的（只有拒绝），设置为拒绝，
     * 其他的都设置为进心中
     *
     * @param flowTask     当前节点
     * @param flowBusiness 业务流程
     */
    private void allPassFlowStatus(FlowTask flowTask, FlowBusiness flowBusiness) {
        BooleanHandel.isTureOrFalse(Target.END.getType().equals(flowTask.getTarget()) &&
                        TaskState.PASS.getType().equals(flowTask.getStatus()))
                .trueOrFalseHandle(() -> flowBusiness.setFlowStatus(FlowStatus.PASS.getType()),
                        () -> BooleanHandel.isTureOrFalse(Target.END.getType().equals(flowTask.getTarget()))
                                .trueOrFalseHandle(() -> flowBusiness.setFlowStatus(FlowStatus.REFUSE.getType()),
                                        () -> flowBusiness.setFlowStatus(FlowStatus.IN_PROGRESS.getType())));
    }

    /**
     * 少数服从多数的处理方式
     *
     * @param flowTask     当前审批节点任务
     * @param flowBusiness 当前流程
     */
    private void majorityFlowStatus(FlowTask flowTask, FlowBusiness flowBusiness) {
        List<FlowTask> flowTasks = flowTaskService.list(new QueryWrapper<FlowTask>().lambda()
                .eq(FlowTask::getTaskId, flowTask.getTaskId()));
        long passNum = flowTasks.stream().filter(item -> TaskState.PASS.getType()
                .equals(item.getStatus())).count();
        long refuseNum = flowTasks.stream().filter(item -> TaskState.REFUSE.getType()
                .equals(item.getStatus())).count();
        BooleanHandel.isTrue(passNum > refuseNum).trueHandle(() -> flowBusiness
                .setFlowStatus(FlowStatus.REFUSE.getType()));
    }

    /**
     * 指定人通过流程通过的处理方式
     *
     * @param flowTask     当前审批节点任务
     * @param flowModel    当前模型
     * @param flowBusiness 当前流程
     */
    private void modelAdminFlowStatus(FlowTask flowTask, FlowModel flowModel, FlowBusiness flowBusiness) {
        FlowTask task = flowTaskService.getOne(new QueryWrapper<FlowTask>().lambda()
                .eq(FlowTask::getBusId, flowTask.getBusId())
                .like(FlowTask::getHeadUsers, flowModel.getFlowModelAdmin()));
        BooleanHandel.isTureOrFalse(TaskState.PASS.getType().equals(task.getStatus())).trueOrFalseHandle(
                () -> flowBusiness.setFlowStatus(FlowStatus.PASS.getType()), () -> {
                    flowBusiness.setFlowStatus(FlowStatus.REFUSE.getType());
                });
    }

}
